﻿using System.Drawing;

namespace ImageClipEmptyRegion
{
    public class ImageClipHelper
    {
        enum PointType
        {
            Invalid, Empty, NotEdge, Have4, OnlyHaveOutline4
        }
        static string GetCopyName(string originName)
        {
            var name = Path.GetFileNameWithoutExtension(originName);
            var dir = Path.GetDirectoryName(originName);
            var ext = Path.GetExtension(originName);
            var copyName = name + "2";

            return Path.Combine(dir, copyName + ext);
        }

        public static void ClipEmptyRegion(string path)
        {
            var copyPath = GetCopyName(path);
            using var bitmap = new Bitmap(path);
            Run(bitmap, copyPath);
        }

        static void Run(Bitmap bitmap, string copyName)
        {
            if (TrySeekStartPoint(bitmap, out var startPoint) == false) return;

            var nextPoints = GetNextPoints(bitmap, startPoint);

            var beginPoint = startPoint;
            var currentPoint = startPoint;
            var prevPoint = nextPoints.First();

            int left = int.MaxValue, right = int.MinValue, top = int.MaxValue, bottom = int.MinValue;
            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

            while (cts.IsCancellationRequested == false)
            {
                left = Math.Min(left, currentPoint.X);
                right = Math.Max(right, currentPoint.X);
                top = Math.Min(top, currentPoint.Y);
                bottom = Math.Max(bottom, currentPoint.Y);
                //Debug.WriteLine($"current point {currentPoint}");
                var nextPoint = GetNextPoint(bitmap, prevPoint, currentPoint);
                prevPoint = currentPoint;
                currentPoint = nextPoint;
                if (currentPoint == beginPoint) break;
            }

            if (cts.IsCancellationRequested)
            {
                Console.WriteLine("时间处理超时");
                return;
            }

            else
            {
                var sourceRect = new Rectangle(left, top, right - left + 1, bottom - top + 1);
                Console.WriteLine($"裁剪后的效果 src rect: {sourceRect}");
                using var bitmapCopy = new Bitmap(sourceRect.Width, sourceRect.Height);
                using var graphic = Graphics.FromImage(bitmapCopy);

                //graphic.DrawRectangle(new Pen(Brushes.Aqua), new Rectangle(0, 0, sourceRect.Width - 1, sourceRect.Height - 1));
                graphic.DrawImage(bitmap, new PointF(-left, -top));
                graphic.Save();
                bitmapCopy.Save(copyName);
            }

        }

        static IEnumerable<Point> GetNextPoints(Bitmap bitmap, Point point)
        {
            var neighbors = GetNeighbors4(point).Where(i => IsEdge(bitmap, i));

            //Debug.WriteLine($"{string.Join(", ", neighbors)}");

            if (neighbors.Count() != 2) throw new Exception($"{point} 存在多个分支！");

            return neighbors.ToArray();
        }

        static Point GetNextPoint(Bitmap bitmap, Point prevPoint, Point currentPoint)
        {
            var neighbors = GetNeighbors4(currentPoint).Where(i => prevPoint != i).Where(i => IsEdge(bitmap, i));
            if (neighbors.Count() == 0) throw new Exception($"{currentPoint} 无法找到下一个点。");
            if (neighbors.Count() != 1) throw new Exception($"{currentPoint} 存在多个分支。");
            return neighbors.First();
        }

        static IEnumerable<Point> GetNeighbors4(Point point)
        {
            yield return new Point(point.X - 1, point.Y + 0);
            yield return new Point(point.X + 1, point.Y + 0);
            yield return new Point(point.X + 0, point.Y - 1);
            yield return new Point(point.X + 0, point.Y + 1);
        }

        static IEnumerable<Point> GetNeighborsOut4(Point point)
        {
            yield return new Point(point.X - 1, point.Y - 1);
            yield return new Point(point.X + 1, point.Y - 1);
            yield return new Point(point.X - 1, point.Y + 1);
            yield return new Point(point.X + 1, point.Y + 1);
        }

        static PointType GetPointType(Bitmap bitmap, Point point)
        {
            if (IsPointInBitmap(bitmap, point) == false) return PointType.Invalid;
            if (IsVisible(bitmap, point) == false) return PointType.Empty;
            if (IsHave4(bitmap, point)) return PointType.Have4;
            if (IsHaveOut4(bitmap, point)) return PointType.OnlyHaveOutline4;
            return PointType.NotEdge;
        }

        static bool IsHave4(Bitmap bitmap, Point point)
        {
            if (IsPointInBitmap(bitmap, point) == false) return false;
            foreach (var neighbor in GetNeighbors4(point))
            {
                // 如果检测到了边缘，或者说周围不可见，说明能够匹配到。
                if (IsPointInBitmap(bitmap, neighbor) == false) return true;
                if (IsVisible(bitmap, neighbor) == false) return true;
            }

            return false;
        }

        static bool IsHaveOut4(Bitmap bitmap, Point point)
        {
            if (IsPointInBitmap(bitmap, point) == false) return false;
            foreach (var neighbor in GetNeighborsOut4(point))
            {
                // 如果检测到了边缘，或者说周围不可见，说明能够匹配到。
                if (IsPointInBitmap(bitmap, neighbor) == false) return true;
                if (IsVisible(bitmap, neighbor) == false) return true;
            }

            return false;
        }

        static bool IsEdge(Bitmap bitmap, Point point)
        {
            var type = GetPointType(bitmap, point);
            return type == PointType.Have4 || type == PointType.OnlyHaveOutline4;
        }

        static bool IsVisible(Bitmap bitmap, Point point)
        {
            if (IsPointInBitmap(bitmap, point) == false) return false;
            var pixel = bitmap.GetPixel(point.X, point.Y);
            return pixel.A != 0;
        }

        /// <summary>
        /// 输入的点是否在图片上。
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        static bool IsPointInBitmap(Bitmap bitmap, Point point)
        {
            if (point.X < 0) return false;
            if (point.Y < 0) return false;
            if (point.X >= bitmap.Width) return false;
            if (point.Y >= bitmap.Height) return false;
            return true;
        }

        /// <summary>
        /// 尝试找到图片中的一处起点。
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="startPoint"></param>
        /// <returns></returns>
        static bool TrySeekStartPoint(Bitmap bitmap, out Point startPoint)
        {
            startPoint = Point.Empty;
            var height = bitmap.Height;
            for (int i = 0; i < height; i++)
            {
                if (TryGetLineLeftPoint(bitmap, i, out startPoint))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 尝试找到这一行的有内容的点。
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="rowId"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        static bool TryGetLineLeftPoint(Bitmap bitmap, int rowId, out Point point)
        {
            point = Point.Empty;
            var width = bitmap.Width;
            for (int i = 0; i < width; i++)
            {
                if (IsVisible(bitmap, new Point(i, rowId)))
                {
                    point = new Point(i, rowId);
                    return true;
                }
            }
            return false;
        }
    }
}
